home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 1995 #5 & #6
/
Amiga Plus CD - 1995 - No. 5 and 6.iso
/
pd
/
serien
/
purity
/
nr.12
/
workshop
/
pascalkursiv.txt
< prev
next >
Wrap
Text File
|
1995-04-21
|
15KB
|
351 lines
========================================================================
= Systemprogrammierung in PCQ-Pascal =
= Kurs für AmigaGadget/Purity - Teil IV =
========================================================================
Bisherige Kursteile :
Teil I : Voraussetzungen,Screens,Windows
Teil II : Einfache Grafikausgabe
Teil III : Weitere Grafikbefehle, einige nützliche Routinen
Erst einmal ein fröhliches Hallo an all die interessierten Pascal-
Programmierer, die sich wieder zusammengefunden haben, um hier der Lust
der Systemprogrammierung zu frönen.
Einen wunderschönen guten Morgen ! Heute wollen wir endlich damit beginnen,
den Anwender in unsere Programme mit einzubeziehen. Da wir Hand in
Hand mit Intuition arbeiten, existiert da für uns natürlich ein Element,
das uns bei geringem Aufwand ein großes Ergebniss liefert - die
IntuitionMessage oder kurz IntuiMessage. Sie arbeitet über sogenannte
IDCMPs, IntuitionDirectCommunictionMessagePorts.
Wie die meisten Intuition-Strukturen ist sie in
"Include:Intuition/Intuition.i" definiert und siehr folgendermaßen aus :
IntuiMessage = record
ExecMessage : Message; die "eigentliche" Messagem wie sie
von Exec verwaltet wird
Class : Integer; nähere Informationen über die Art
der Message (z.dt. Nachricht)
Code : Short; speziellere Informationen über die
Message, z.B. bei gedrückten
Mausknopf, ob er losgelassen wurde
oder gedrückt, etc.
Qualifier : Short; bei Tastaturabfrage Informationen
über benutzte Sondertasten etc.
IAddress : Address; enthält spezielle Informationen, wie
z.B. die Adresse des aktivierten
Gadgets
MouseX, die Mauskoordinaten, bei Empfang
MouseY : Short; der Message
Seconds, die Werte der Systemuhr zur Zeit
Micros : Integer; des Empfangs der Nachricht
IDCMPWindow : Address; die Adresse des Windows, aus dem
die Message kam
SpecialLink : ^IntuiMessage;
end;
IntuiMessagePtr = ^IntuiMessage;
Sieht komplex und kompliziert aus, erweist sich jedoch im täglichen
Gebrauch als sehr praktisch und einfach.
Wie kommen wir nun an so eine Message ran ?
Es empfiehlt sich - der Einfachkeit halber - ersteinmal mit den uns
aus diesem Kurs schon bekannten Elementen zu arbeiten - in unserem
Fall mit Windows.
Wir erinnern uns, oder lesen es nach, daß wir im Teil 1 dieses Kurses
ein Element der NewWindow-Struktur ausdrücklich aufgespart haben, eine
Integer-Variable des Namens IDCMPFlags. Nun können wir etwas mehr damit
anfangen, wissen wir doch jetzt grob, was IDCMP für uns bedeutet.
Die Vermutung liegt nahe, daß man dieses Feld mit Informationen
füllen kann, die die Art der IntuiMessages bestimmt, die in diesem
Window empfangen werden können. Das ist sehr sinnvoll, da es eine
Unzahl von IntuiMessages gibt und es für den Programmierer enorm
aufwendig wäre, stets nur die gewünschten herauszufiltern. Eine
kurze Übersicht über die wichtigsten der IDCMP-Flags, natürlich in
"Include:Intuition/Intuition.i" zu finden :
SIZEVERIFY_f wenn der Benutzer versucht, die Größe des
Fensters zu ändern, wird diese Message gesendet.
Intuition wartet so lange, bis man diese
IntuiMsg beantwortet hat.
NEWSIZE_f IntuiMsg wird gesendet, wenn die Größe des
Fensters vom Benutzer geändert wurde
REFRESHWINDOW_f spielt bei Windows eine Rolle, die nur über
SIMPLE_REFRESH verfügen und wird gesendet, wenn der
Fensterinhalt zerstört wurde, der Programmierer sich
also darum kümmern muß, ihn wieder aufzubauen.
MOUSEBUTTONS_f diese IntuiMsg erhält man, wenn der Benutzer
die linke Maustaste gedrückt hat. Hat man auch
noch das Flag RMBTRAP in der entsprechenden
NewWindowStruktur definiert, so erhält man diese
Message auch bei Betätigung des rechten Mausknopfs.
Nähere Informationen zu der Art dieser Message
stehen im Feld Code des IntuiMsg :
SELECTUP : linke Maustaste losgelassen
SELECTDOWN : linke Maustaste gedrückt
MENUUP : rechte Maustaste losgelassen
MENUDOWN : rechte Maustaste gedrückt
MOUSEMOVE_f sollte gesetzt sein, wenn man in der IntuiMessage
-Struktur die Mauskoordinaten aktualisiert
haben möchte.
GADGETDOWN_f ein Gadget wurde angeklickt
GADGETUP_f ein Gadget wurde wieder losgelassen (die linke
Maustaste wurde über einem Gadget losgelassen)
MENUPICK_f ein Menüpunkt wurde ausgewählt, nähere
Informationen darüber in Variable Code und wenn wir
uns in diesem Kurs näher damit beschäftigen.
CLOSEWINDOW_f das CloseGadget des Windows wurde angewählt
RAWKEY_f eine Taste wurde gedrückt. Der Tastaturcode (nicht
der Ascii-Code) befindet sich in der Variable Code.
NEWPREFS_f es wurden einige Preferences-Werte vom Benutzer
geändert
DISKINSERTED_f eine Diskette wurde in ein Laufwerk eingelegt
DISKREMOVED_f eine Diskette wurde einem Laufwerk entnommen
ACTIVEWINDOW_f ein Fenster wurde aktiviert
INACTIVEWINDOW_f ein Fenster wurde deaktiviert
VANILLAKEY_f wie RAWKEY_f, nur daß diesmal der ASCII-Code
in Code gespeichert wird
INTUITICKS_f solange das Fenster geöffnet ist, behämmert
Intuition den Programmierer mit diesem Signal
Das "_f" am Ende dient übrigens dazu, die Flags von oft gleichnamigen
Intuition-Prozeduren und -Funktionen zu unterscheiden.
Die Flags müssen in das IDCMP-Feld der NewWindow-Struktur eingetragen
werden und stehen dei Empfang einer Nachricht im Feld "Class" !
Nun kommen wir zu einem Beispiel :
Wir wollen ein Fenster aufmachen, das durch Druck auf das Closegadget
geschlossen wird und dabei das Programm beendet.
"Einfach" möchte man vorschnell denken und man hätte sogar recht :-)
Denken wir uns die Schritte durch :
1. Fensterstruktur festlegen, dabei muß das Feld IDCMPFlags mit
CLOSEWINDOW_f gefüllt werden.
2. Öffnen des Fensters.
3. Auf die Message warten, daß das Gadget betätigt wurde.
4. Fenster schließen und Ende.
Davon abgesehen, daß in Sachen Punkt 3 noch keine weiteren Informationen
gegeben sind, ist in diesem Ablaufplan ein Fehler. Aufmerksame Leser
(Folge I !!) werden ihn bemerkt haben.
Er liegt in Punkt 1 :
der User könnte so lange er wollte die linke obere Ecke des Fensters
anstarren, es würde einfach kein CloseGadget entstehen. Dazu muß man
nämlich bekanntermaßen in der NewWindow-Struktur das WINDOWCLOSE-Flag
setzen !
Nun zu dem alles entscheidenden Punkt 3. Hier stellt Exec uns
z.B. folgende Befehle zur Verfügung :
MessagePtr := GetMsg(port : MsgPortPtr);
Diese Funktion holt die nächste Message aus einem
MessagePort, einer Art "Hafen" für ankommende Nachrichten.
Sie stehen dort Schlange, bis jemand sie nimmt und
zurückschickt.
Da die Message-Befehle in "Include:exec/Ports.i"
definiert sind (wird allerdings von "Include:intuition/
intuition.i" automatisch eingebunden), müssen es nicht
unbedingt IntuiMessages sein, die empfangen werden.
GetMsg liefert also keinen IntuiMessagePtr zurück !
Darauf muß geachtet werden, da sonst bei der
Compilierung eine Fehlermeldung auftritt !
Der MsgPort, an den "unsere" Messages ankommen, befindet
sich im Window und heißt "UserPort", weil er eben die
Schnittstelle zum User, dem Anwender, ist.
ReplyMsg(mess : MessagePtr);
Wie oben beschrieben, sollten einmal angenommene
Messages wieder zurückgeschickt werden, da sie
Speicherplatz belegen. Bildlich gesprochen, gammeln
sie immer noch in der Schlange herum....
Hier gilt wieder : Exec bezieht sich NICHT auf
IntuiMessages !
Nun kommen wir zu einem häufigen Problem im Umgang mit Messages, das
auch in unserem Programm auftritt. Wir wollen ja warten, bis der User
sich bequemt, das Gadget anzuklicken. Wie realisiert man nun das ?
Nun ja, eine Möglichkeit wäre
REPEAT
myintuimessage:=Address (GetMsg (mywindow^.UserPort));
UNTIL myintuimessage<>NIL;
Dies ist eigentlich logisch und auch schön strukturiert. Auf einem PC
mag dies das Maß aller Dinge sein. Nicht so auf unserem Amiga, der ja
bekanntermaßen ein Multitasking-System ist, ein Computer also, auf dem
viele Programme gleichzeitig laufen können.
Benutzt man nun obige Routine in einem Programm, so zeigt es sich, daß
parallel laufende Programme extrem langsamer werden, der Prozessor also
"in die Knie geht". Dies liegt daran, das hier ständig ein Befehl
abgearbeitet wird. Einfacher und wesentlich systemkonformer geht es mit
der Exec-Routine
MessagePtr := WaitPort (port : MsgPortPtr);
Sie wartet so lange, bis eine Message im MsgPort ankommt.
Bis dahin wird der Prozessor NICHT belastet !
Das ist ein enormer Vorteil.
Unsere Warteroutine sieht nun also so aus :
myintuimessage:=Address (WaitPort (mywindow^.UserPort));
myintuimessage:=Address (GetMsg (mywindow^.UserPort));
Nun werfen sich noch zwei Fragen auf :
1. Was sollen eigentlich das "Address" vor den Funktionsaufrufen und
was sollen die Klammern drumherum ?
2. Warum folgt auf das WaitPort nochmal ein GetMsg ?
Die Antworten :
1. Das "Address" ist ein sogenannter "Cast", der aus der speziellen
Erwartung "MessagePtr" (was ja nicht zu IntuiMessagePtr passt !), eine
allgemeine Address macht, die auch mit einem IntuiMessagePtr
gleichgesetzt werden kann, ohne daß PCQ eine Fehlermeldung ausgibt.
Die Klammern um den Funktionsaufruf kennzeichnen die Variable, in
diesem Fall den Rückgabeparameter der Funktion, die "gecastet" werden
soll.
2. WaitPort liefert NICHT den Zeiger auf die angekommene Message zurück !
Diesen Fehler darf man AUF KEINEN Fall begehen ! Deshalb : nach jedem
WaitPort muß ein GetMsg kommen !
Hier möchte ich auch gleich darauf hinweisen, daß ich aus diesem
Grund mein "Include:Exec/Ports.i"-File ein wenig modifiziert habe,
und dort nun nur noch zu lesen ist :
Procedure WaitPort (port : MessagePortPtr);
Nur für diesen Kurs schreibe ich die WaitPorts in die ursprüngliche
Form um und deshalb kann es sein, daß ich es einmal vergesse und
in einem Beispiel auch eine WaitPort-Prozedur, statt einer
Funktion aufrufe ! Dies bitte ich im voraus zu entschuldigen !
Nun aber, nach so viel grauer Theorie, zu dem Beispielprogramm :
Program KursProgramm;
{ Listing für den AMIGAGadget/Purity - Pascalkurs , }
{ Erste Schritte in der Messageprogrammierung }
{$I "Include:Intuition/Intuition.I" }
{$I "Include:Exec/Libraries.I" }
VAR
myscreen : ScreenPtr;
mywindow : WindowPtr;
PROCEDURE CloseDisplay;
VAR i : Integer;
{ Sollte ein Teil des Displays offen sein, dann wird er von }
{ dieser Routine geschlossen. }
BEGIN
IF mywindow<>NIL THEN
CloseWindow (mywindow);
IF myscreen<>NIL THEN
CloseScreen (myscreen);
END;
PROCEDURE BreakProgram (reason : STRING);
{ Diese Routine schließt alles bisher geöffnete, druckt den }
{ Fehlergrund aus und bricht dann das Programm ab. }
BEGIN
CloseDisplay;
WRITELN ('Program error : ',reason);
Exit (42);
END;
PROCEDURE OpenDisplay;
{ Diese Routine erstellt ein Display. }
CONST
mynewscreen : NewScreen = (0,0,640,256,2,0,1,HIRES,
CUSTOMSCREEN_f,NIL,
NIL,NIL,NIL);
NewWin : NewWindow = (160,50,320,128,0,1,CLOSEWINDOW_f,
SMART_REFRESH+WINDOWDRAG+
ACTIVATE+SMART_REFRESH+WINDOWCLOSE,NIL,NIL,
"Click to Close - Dank Messages !",
NIL,NIL,0,0,0,0,CUSTOMSCREEN_f);
BEGIN
myscreen := OpenScreen (Adr(mynewscreen));
IF myscreen=NIL THEN BreakProgram ("Couldn't open Screen");
NewWin.Screen:=myscreen;
mywindow := OpenWindow (Adr(NewWin));
IF mywindow=NIL THEN BreakProgram ("Couldn't open Window");
END;
PROCEDURE WarteAufKlick;
{ Unsere, oben entwickelte Warte-Routine. }
VAR myintuimessage : IntuiMessagePtr;
BEGIN
myintuimessage := Address (WaitPort (mywindow^.UserPort));
myintuimessage := Address (GetMsg (mywindow^.UserPort));
ReplyMsg (Address(myintuimessage));
{ schön brav die Message zurückschicken und dann good bye ! }
END;
BEGIN
OpenDisplay;
WarteAufKlick;
CloseDisplay;
END.
Es steht nun jedem frei, selbst mit den verschiedensten Flags zu
experimentieren, bis wir im nächsten Teil tiefer in die Materie
einsteigen.
Weitere Informationen, Antworten auf Fragen und und und gibt es
natürlich im Leserforum des Gadgets oder direkt bei mir
Andreas Neumann
Auf dem Ruhbühl 151
7997 Immenstaad
Ansonsten bis zur nächsten Purity, wenn es wieder heißt
Ewig währt am längsten oder
wie programmiere ich Pascal ?
© 1992 by Andreas Neumann für AmigaGadget von Nils Kassube
und Purity von Steppenbrand und Diesel